﻿using AbpProjectTemplate.EntityFrameworkCore;
using AbpProjectTemplate.Localization;
using AbpProjectTemplate.MultiTenancy;
using AbpProjectTemplate.OpenIddict;
using Jy.Abp.AspNetCore.Cors;
using Jy.Abp.AspNetCore.Serilog;
using Jy.Abp.AspNetCore.Swagger;
using LogDashboard;
using LogDashboard.Authorization.Filters;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using OpenIddict.Server.AspNetCore;
using OpenIddict.Validation.AspNetCore;
using System;
using System.IO;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
using Volo.Abp.AspNetCore.Mvc.Libs;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.Autofac;
using Volo.Abp.AutoMapper;
using Volo.Abp.BlobStoring;
using Volo.Abp.BlobStoring.FileSystem;
using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict;
using Volo.Abp.Security.Claims;
using Volo.Abp.Swashbuckle;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;

namespace AbpProjectTemplate.Web;

[DependsOn(
    typeof(AppHttpApiModule),
    typeof(AppApplicationModule),
    typeof(AppEntityFrameworkCoreModule),
    typeof(JyAbpAspNetCoreSerilogModule),

    typeof(AbpAutofacModule),
    typeof(AbpBlobStoringFileSystemModule),
    //typeof(AbpAccountWebModule),
    //typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule),
    typeof(AbpIdentityAspNetCoreModule),
    typeof(AbpOpenIddictAspNetCoreModule)
    )]
public class AppWebModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        var environment = context.Services.GetHostingEnvironment();

        PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
        {
            options.AddAssemblyResource(
                typeof(AppResource),
                typeof(AppDomainModule).Assembly,
                typeof(AppDomainSharedModule).Assembly,
                typeof(AppApplicationModule).Assembly,
                typeof(AppApplicationContractsModule).Assembly,
                typeof(AppWebModule).Assembly
            );
        });

        PreConfigure<OpenIddictServerBuilder>(builder =>
        {
            var serverConfig = context.Services
                        .GetConfiguration()
                        .GetSection("Authentication:OpenIddictServer");
            var accessTokenExpireMinutes = serverConfig
                                    .GetSection("AccessTokenExpireMinutes")
                                    .Get<int?>() ?? 120;
            builder.SetAccessTokenLifetime(TimeSpan.FromMinutes(accessTokenExpireMinutes));

            //处理签名key
            var signingKey = serverConfig["SigningKey"];
            if (signingKey.HasValue())
                builder.AddSigningKey(OpeniddictHelper.CreateKey(signingKey));
            var signingCertificate = OpeniddictHelper.CreateOrGetCertificate
            (
                path: Path.Combine(environment.ContentRootPath, "App_Data", "openIddict-signing.pfx"),
                type: OpeniddictHelper.CertificateType.Signing,
                expTime: TimeSpan.FromDays(100000)
            );
            builder.AddSigningCertificate(signingCertificate);

            //处理加密
            var encryptionKey = serverConfig["EncryptionKey"];
            if (encryptionKey.HasValue())
                builder.AddEncryptionKey(OpeniddictHelper.CreateKey(encryptionKey));
            var encryptionCertificate = OpeniddictHelper.CreateOrGetCertificate
            (
                path: Path.Combine(environment.ContentRootPath, "App_Data", "openIddict-encryption.pfx"),
                type: OpeniddictHelper.CertificateType.Encryption,
                expTime: TimeSpan.FromDays(100000)
            );
            builder.AddEncryptionCertificate(encryptionCertificate);
        });

        PreConfigure<OpenIddictBuilder>(builder =>
        {
            builder.AddValidation(options =>
            {
                options.AddAudiences(OpenIddictConstant.AppResource);
                options.UseLocalServer();
                options.UseAspNetCore();
            });
        });
    }

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        var configuration = context.Services.GetConfiguration();
        ConfigureAuthentication(context);
        ConfigureUrls(configuration);
        ConfigureAutoMapper();
        ConfigureVirtualFileSystem(hostingEnvironment);
        ConfigureBlobStoring(configuration, hostingEnvironment);
        ConfigureAspNetCore();
        ConfigureSwaggerServices(context.Services, configuration);
        ConfigureCors(context.Services);
        ConfigureLogDashboard(context.Services, configuration);
        ConfigureLocalization();
    }

    private void ConfigureAuthentication(ServiceConfigurationContext context)
    {
        context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
        this.Configure<OpenIddictServerAspNetCoreOptions>(options =>
        {
            options.DisableTransportSecurityRequirement = true;
            options.SuppressJsonResponseIndentation = false;
        });

        this.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
        {
            options.IsDynamicClaimsEnabled = true;
        });

        this.Configure<AbpOpenIddictAspNetCoreOptions>(options =>
        {
            options.AddDevelopmentEncryptionAndSigningCertificate = false;
        });
    }

    private void ConfigureUrls(IConfiguration configuration)
    {
        Configure<AppUrlOptions>(options =>
        {
            options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
        });
    }

    private void ConfigureAutoMapper()
    {
        Configure<AbpAutoMapperOptions>(options =>
        {
            options.AddMaps<AppWebModule>();
        });
    }

    private void ConfigureVirtualFileSystem(IWebHostEnvironment hostingEnvironment)
    {
        if (hostingEnvironment.IsDevelopment())
        {
            Configure<AbpVirtualFileSystemOptions>(options =>
            {
                options.FileSets.ReplaceEmbeddedByPhysical<AppDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}AbpProjectTemplate.Domain.Shared"));
                options.FileSets.ReplaceEmbeddedByPhysical<AppDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}AbpProjectTemplate.Domain"));
                options.FileSets.ReplaceEmbeddedByPhysical<AppApplicationContractsModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}AbpProjectTemplate.Application.Contracts"));
                options.FileSets.ReplaceEmbeddedByPhysical<AppApplicationModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}AbpProjectTemplate.Application"));
                options.FileSets.ReplaceEmbeddedByPhysical<AppWebModule>(hostingEnvironment.ContentRootPath);
            });
        }
    }

    private void ConfigureAspNetCore()
    {
        Configure<AbpMvcLibsOptions>(options =>
        {
            options.CheckLibs = false;
        });
        Configure<AbpAntiForgeryOptions>(options =>
        {
            options.AutoValidate = false;
        });
        Configure<AbpAspNetCoreMvcOptions>(options =>
        {
            options.ConventionalControllers.Create(typeof(AppApplicationModule).Assembly);
        });
    }

    private void ConfigureSwaggerServices(IServiceCollection services, IConfiguration configuration)
    {
        services.AddAbpSwaggerGenWithOidc(
           authority: configuration["App:SelfUrl"],
           scopes: [OpenIddictConstant.AppScope],
           flows: [AbpSwaggerOidcFlows.Password],
           setupAction: options =>
           {
               options.ConfigureDefault();
           });
    }

    private void ConfigureCors(IServiceCollection services)
    {
        var origins = services.GetConfiguration()
            .GetSection("Cors:Origins")
            .Get<string[]>();
        services.AddCors(options => options.ConfigureDefault(origins));
    }

    private void ConfigureLogDashboard(IServiceCollection services, IConfiguration configuration)
    {
        var rootFolder = configuration["LogDashboard:RootFolder"];
        var pathMatch = configuration["LogDashboard:PathMatch"];
        var admin = configuration["LogDashboard:UserName"];
        var password = configuration["LogDashboard:Password"];
        services.AddLogDashboard(options =>
        {
            options.RootPath = Path.Combine(Directory.GetCurrentDirectory(), rootFolder);
            options.PathMatch = pathMatch;
            options.AddAuthorizationFilter(new LogDashboardBasicAuthFilter(admin, password));
        });
    }

    private void ConfigureBlobStoring(IConfiguration configuration, IWebHostEnvironment environment)
    {
        var blobConfig = configuration.GetSection("BlobStoring");
        this.Configure<AbpBlobStoringOptions>(options =>
        {
            options.Containers.ConfigureDefault(cfg =>
            {
                cfg.UseFileSystem(fs =>
                {
                    var path = Path.Combine(environment.ContentRootPath, blobConfig["FileSystem:BaseFolder"]);
                    if (!Directory.Exists(path))
                    {
                        Directory.CreateDirectory(path);
                    }
                    fs.BasePath = path;
                });
            });
        });
    }

    private void ConfigureLocalization()
    {

    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();
        var logDashboardOptions = context.ServiceProvider
            .GetRequiredService<LogDashboardOptions>();

        //if (env.IsDevelopment())
        //    app.UseDeveloperExceptionPage();
        //else
        //    app.UseErrorPage();

        app.UseCorrelationId();
        app.UseAbpSecurityHeaders();
        app.UseStaticFiles();
        app.UseStaticFiles(new StaticFileOptions
        {
            FileProvider = new PhysicalFileProvider(
                    Path.Combine(env.ContentRootPath, ".well-known")),
            RequestPath = "/.well-known",
            ServeUnknownFileTypes = true
        });
        app.UseAbpRequestLocalization();
        app.UseCors();
        app.UseLogDashboard(logDashboardOptions.PathMatch);
        app.UseRouting();
        app.UseAuthentication();
        app.UseAbpOpenIddictValidation();
        if (MultiTenancyConsts.IsEnabled)
        {
            app.UseMultiTenancy();
        }
        //app.UseUnitOfWork();
        app.UseDynamicClaims();
        app.UseAuthorization();
        app.UseSwagger();
        app.UseAbpSwaggerUI(options =>
        {
            options.ConfigureDefault(context.GetAppName());
            options.OAuthClientId(OpenIddictConstant.DefaultClientId);
            options.OAuthScopes(OpenIddictConstant.AppScope);
        });
        app.UseAuditing();
        app.UseAbpSerilogEnrichers();
        app.UseConfiguredEndpoints();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/", context =>
            {
                context.Response.Redirect("/swagger");
                return Task.CompletedTask;
            });
        });
    }
}
